package org.tangerinejunction.imstats.ui;

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Calendar;

import javax.swing.JFrame;

import org.tangerinejunction.imstats.data.IMDataset;
import org.tangerinejunction.imstats.data.TableFactory;
import org.tangerinejunction.imstats.logs.EntityWriter;
import org.tangerinejunction.imstats.logs.AdiumLogReader;
import org.tangerinejunction.imstats.ui.menu.MainMenu;
import org.tangerinejunction.imstats.util.Cycle;
import org.tangerinejunction.imstats.util.Increment;
import org.tangerinejunction.imstats.util.TimeRange;

import prefuse.data.Table;

/**
 * This is the main class that runs the entire application.
 * In the old UI it creates two table windows and two chart windows.
 * In the new UI (under development) it creates a control panel
 * and three chart windows, and the chart windows are connected
 * to their associated tables.
 * 
 * @author meganthorsen
 */
public class Application extends JFrame
{
	public static File logBaseFolder;
	public static File objectFolder;
	public static final int WINDOW_HEIGHT = 350;
	public static final int TABLE_WINDOW_WIDTH = 650;
	
	private static final long serialVersionUID = -2455600007936459819L;
	
	IMDataset imDataset;
	MainMenu mainMenu;
	Preferences preferences;
	
	ControlPanel controlPanel;
	SummaryFrame summaryFrame;
	TrendFrame trendFrame;
	CycleFrame cycleFrame;
	CorrespondentStatsFrame correspondentStatsFrame;
	
	/**
	 * Currently this reads the log data every time you create the Application.
	 */
	public Application()
	{
	    logBaseFolder = new File(System.getProperty("user.home") + "/Library/Application Support/Adium 2.0/Users/Default/Logs");
	    objectFolder = new File(System.getProperty("user.home") +"/Library/Application Support/Adium 2.0/Users/Default/ByObject");

	    preferences = Preferences.getInstance();
		imDataset = new IMDataset();
		IMDataset.lastRead = Calendar.getInstance();
		AdiumLogReader.readLogs(logBaseFolder, objectFolder, imDataset);
	
		//initializeData();
		createComponents();
		layoutComponents();
		setupEvents();
	}
	
	/*
	public void initializeData()
	{
		if ((new File("entityTable.imstat").exists()))
		{
			try 
			{
				FileInputStream fis = new FileInputStream("entityTable.imstat");
				imDataset.readExternal(new ObjectInputStream(fis));
			} 
			catch (IOException e) 
			{
				e.printStackTrace();
			}
			catch (ClassNotFoundException e) 
			{
				e.printStackTrace();
			}
		}
	}
	*/
	
	private void createComponents()
	{
		mainMenu = new MainMenu(this);
		Calendar endDate = Calendar.getInstance();
		endDate.set(endDate.get(Calendar.YEAR), endDate.get(Calendar.MONTH), endDate.get(Calendar.DATE), 23, 59, 59);
		Calendar startDate = Calendar.getInstance();
		startDate.set(endDate.get(Calendar.YEAR), endDate.get(Calendar.MONTH), endDate.get(Calendar.DATE), 0, 0, 0);
		// View the last 8 weeks
		startDate.add(Calendar.DATE, -55);
		TimeRange range = new TimeRange(startDate, endDate);
		createGUI(range, startDate, endDate);
	}
	
	private void createGUI(TimeRange range, Calendar startDate, Calendar endDate)
	{
		controlPanel = new ControlPanel(this, imDataset, ControlPanel.ENTITY_VIEW, range, Increment.WEEK);
		summaryFrame = new SummaryFrame(imDataset, imDataset.getEntityTable(), preferences);
		trendFrame = new TrendFrame(imDataset, preferences, ControlPanel.ENTITY_VIEW, startDate, endDate, Increment.WEEK, 0);
		cycleFrame = new CycleFrame(imDataset, preferences, ControlPanel.ENTITY_VIEW, startDate, endDate, Cycle.DAY_OF_WEEK, 0);
		correspondentStatsFrame = new CorrespondentStatsFrame(imDataset, preferences, ControlPanel.ENTITY_VIEW, startDate, endDate);
		controlPanel.updateTable(true);
	}
	
	private void layoutComponents()
	{
		setLayout(new BorderLayout());
		setTitle("Control Panel ");
		setJMenuBar(mainMenu);
		
		layoutGUI();
	}

	private void layoutGUI()
	{
		add(controlPanel);
		pack();
		//controlPanel.pack();
		//controlPanel.setLocation(0, 0);
		//controlPanel.setVisible(true);
		summaryFrame.pack();
        trendFrame.pack();
        cycleFrame.pack();
        correspondentStatsFrame.pack();
		// TODO: fix the magic number here
		summaryFrame.setLocation(0, 245);
		summaryFrame.setVisible(true);
        correspondentStatsFrame.setLocation(getWidth()+30*2, 50*2);
        correspondentStatsFrame.setVisible(true);
        cycleFrame.setLocation(getWidth()+30, 50);
        cycleFrame.setVisible(true);
        trendFrame.setLocation(getWidth(), 0);
        trendFrame.setVisible(true);
	}
	
	private void setupEvents()
	{
		addWindowListener(new WindowAdapter()
		{
			protected void processWindowStateEvent(WindowEvent e) 
			{
				if (e.getNewState() == WindowEvent.WINDOW_ACTIVATED)
					System.err.println("Window activated");
				if (e.getNewState() == WindowEvent.WINDOW_CLOSED)
					System.err.println("Window closed");
				if (e.getNewState() == WindowEvent.WINDOW_CLOSING)
					System.err.println("Window closing");
				if (e.getNewState() == WindowEvent.WINDOW_DEACTIVATED)
					System.err.println("Window deactivated");
				if (e.getNewState() == WindowEvent.WINDOW_DEICONIFIED)
					System.err.println("Window deiconified");
				if (e.getNewState() == WindowEvent.WINDOW_FIRST)
					System.err.println("Window first");
				if (e.getNewState() == WindowEvent.WINDOW_GAINED_FOCUS)
					System.err.println("Window gained focus");
				if (e.getNewState() == WindowEvent.WINDOW_ICONIFIED)
					System.err.println("Window iconified");
				if (e.getNewState() == WindowEvent.WINDOW_LAST)
					System.err.println("Window last");
				if (e.getNewState() == WindowEvent.WINDOW_LOST_FOCUS)
					System.err.println("Window lost focus");
				if (e.getNewState() == WindowEvent.WINDOW_OPENED)
					System.err.println("Window opened");
				if (e.getNewState() == WindowEvent.WINDOW_STATE_CHANGED)
					System.err.println("Window state changed");
			}
			
			protected void processWindowEvent(WindowEvent e)
			{
				if (e.getNewState() == WindowEvent.WINDOW_ACTIVATED)
					System.err.println("Window activated");
				if (e.getNewState() == WindowEvent.WINDOW_CLOSED)
					System.err.println("Window closed");
				if (e.getNewState() == WindowEvent.WINDOW_CLOSING)
					System.err.println("Window closing");
				if (e.getNewState() == WindowEvent.WINDOW_DEACTIVATED)
					System.err.println("Window deactivated");
				if (e.getNewState() == WindowEvent.WINDOW_DEICONIFIED)
					System.err.println("Window deiconified");
				if (e.getNewState() == WindowEvent.WINDOW_FIRST)
					System.err.println("Window first");
				if (e.getNewState() == WindowEvent.WINDOW_GAINED_FOCUS)
					System.err.println("Window gained focus");
				if (e.getNewState() == WindowEvent.WINDOW_ICONIFIED)
					System.err.println("Window iconified");
				if (e.getNewState() == WindowEvent.WINDOW_LAST)
					System.err.println("Window last");
				if (e.getNewState() == WindowEvent.WINDOW_LOST_FOCUS)
					System.err.println("Window lost focus");
				if (e.getNewState() == WindowEvent.WINDOW_OPENED)
					System.err.println("Window opened");
				if (e.getNewState() == WindowEvent.WINDOW_STATE_CHANGED)
					System.err.println("Window state changed");
				if (e.getNewState() == WindowEvent.WINDOW_CLOSING ||
						e.getNewState() == WindowEvent.WINDOW_CLOSED)
					dispose();
			}
		});
	}
	
	public Preferences getPreferences() 
	{
		return preferences;
	}
	
	/**
	 * The main function merely creates the Application.
	 * @param args	any arguments to parse; none currently supported
	 */
	public static void main(String[] args)
	{
		Application application = new Application();
		application.setVisible(true);
	}
	
	/**
	 * Update and display the Summary chart.
	 */
	public void showSummaryChart()
	{
		summaryFrame.updateDataTable(controlPanel.dataTable);
		summaryFrame.setVisible(true);
	}
	
	/**
	 * Update and display the Trend chart.
	 */
	public void showTrendChart()
	{
		trendFrame.updateDataTable(controlPanel.dataTable, controlPanel.getStartCalendar(), 
				controlPanel.getEndCalendar(), trendFrame.increment, trendFrame.numIncrementColumns);
		trendFrame.removeIncrementColumns();
		trendFrame.addIncrementColumns();
		trendFrame.setVisible(true);
	}
	
	/**
	 * Update and display the Cycle chart.
	 */
	public void showCycleChart()
	{
		cycleFrame.updateDataTable(controlPanel.dataTable, controlPanel.getStartCalendar(), 
				controlPanel.getEndCalendar(), cycleFrame.cycle, cycleFrame.numIncrementColumns);
		cycleFrame.setVisible(true);
	}
    
    /**
     * Update and display the Correspondent Stats chart.
     */
    public void showCorrespondentStatsChart()
    {
        correspondentStatsFrame.updateDataTable(controlPanel.dataTable, controlPanel.getStartCalendar(), controlPanel.getEndCalendar());
        correspondentStatsFrame.removeIncrementColumns();
        correspondentStatsFrame.addIncrementColumns();
        correspondentStatsFrame.setVisible(true);
    }
	
	/**
	 * Save all Entity information to a file.
	 */
	public void saveData()
	{	
		preferences.saveProperties();
		EntityWriter.writeEntityFile(imDataset);
		/*
		try 
		{
			FileOutputStream fos = new FileOutputStream("entityTable.imstat");
			imDataset.writeExternal(new ObjectOutputStream(fos));
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		*/
	}
	
	/**
	 * Save the Entity data when disposing of this Application.
	 */
	public void dispose()
	{
		saveData();
		super.dispose();
	}
	
	/**
	 * Update the data table according to ControlPanel parameter changes.
	 * Only update open charts.
	 * 
	 * @param init	whether we are initializing the Application
	 */
	public void updateDataTable(boolean init)
	{
		if (summaryFrame != null && (summaryFrame.isVisible() || init))
			summaryFrame.updateDataTable(controlPanel.dataTable);
		if (trendFrame != null && (trendFrame.isVisible() || init))
			trendFrame.updateDataTable(controlPanel.dataTable);
		if (cycleFrame != null && (cycleFrame.isVisible() || init))
			cycleFrame.updateDataTable(controlPanel.dataTable);
        if (correspondentStatsFrame != null && (correspondentStatsFrame.isVisible() || init))
            correspondentStatsFrame.updateDataTable(controlPanel.dataTable);
	}

	/**
	 * Add any increment columns to the tables driving the charts.
	 */
	public void addIncrementColumns()
	{
		addIncrementColumns(false);
	}

	/**
	 * Add any increment columns to the tables driving the charts.
	 * 
	 * @param init	whether we are initializing this Application
	 */
 	public void addIncrementColumns(boolean init)
 	{
 		if (summaryFrame != null && (summaryFrame.isVisible() || init))
 			summaryFrame.updateDataTable(controlPanel.dataTable);
        if (correspondentStatsFrame != null && (correspondentStatsFrame.isVisible() || init))
        {
            correspondentStatsFrame.updateDataTable(controlPanel.dataTable);
            correspondentStatsFrame.addIncrementColumns();
        }
 		if (trendFrame != null && (trendFrame.isVisible() || init))
 		{
 			trendFrame.updateDataTable(controlPanel.dataTable, controlPanel.getStartCalendar(), 
	 				controlPanel.getEndCalendar(), (Increment)trendFrame.incrementSelector.getSelectedItem(), trendFrame.numIncrementColumns);
 			trendFrame.addIncrementColumns();
 			/*
	 		String valueColumnName = (String)trendFrame.valueSelector.getSelectedItem();
	 		Increment increment = (Increment)trendFrame.incrementSelector.getSelectedItem();
	
			Calendar start = (Calendar)controlPanel.getStartCalendar().clone();
			Calendar mid = (Calendar)controlPanel.getStartCalendar().clone();
			while (mid.before(controlPanel.getEndCalendar()))
			{
	 			mid.add(Calendar.DATE, increment.numDays);
	 			mid.add(Calendar.MONTH, increment.numMonths);
	 			mid.add(Calendar.YEAR, increment.numYears);
	 			TableFactory.addColumnToTable(valueColumnName, controlPanel.dataTable, new TimeRange(start, mid), true);
	 			start.add(Calendar.DATE, increment.numDays);
				start.add(Calendar.MONTH, increment.numMonths);
				start.add(Calendar.YEAR, increment.numYears);
				trendFrame.numIncrementColumns++;
			}
			TableFactory.calculateConsistencyColumn(controlPanel.dataTable, valueColumnName,
					controlPanel.dataTable.getColumnCount()-trendFrame.numIncrementColumns, controlPanel.dataTable.getColumnCount());
	 		
	 		summaryFrame.updateDataTable(controlPanel.dataTable);
	 		trendFrame.updateDataTable(controlPanel.dataTable, controlPanel.getStartCalendar(), 
	 				controlPanel.getEndCalendar(), increment, trendFrame.numIncrementColumns);
	 				*/
 		}
 		if (cycleFrame != null && (cycleFrame.isVisible() || init))
 		{
 			cycleFrame.updateDataTable(controlPanel.dataTable, controlPanel.getStartCalendar(), 
	 				controlPanel.getEndCalendar(), (Cycle)cycleFrame.cycleSelector.getSelectedItem(), cycleFrame.numIncrementColumns);
 			cycleFrame.addIncrementColumns();
	 	}
 	}

 	/**
 	 * Remove any increment columns from the tables driving the charts.
 	 */
 	public void removeIncrementColumns()
 	{
 		removeIncrementColumns(false);
 	}

 	/**
 	 * Remove any increment columns from the tables driving the charts.
 	 * 
 	 * @param init	whether we are initializing this Application
 	 */
 	public void removeIncrementColumns(boolean init)
 	{
 		if (trendFrame != null && (trendFrame.isVisible() || init))
 		{
 			trendFrame.removeIncrementColumns();
 			/*
	 		int colCount = controlPanel.dataTable.getColumnCount();
	 		for (int i = colCount-1; i >= colCount-trendFrame.numIncrementColumns; --i)
	 			controlPanel.dataTable.removeColumn(controlPanel.dataTable.getColumn(i));
	 		if (trendFrame != null)
	 			trendFrame.numIncrementColumns = 0;
	 			*/
 		}
 		if (cycleFrame != null && (cycleFrame.isVisible() || init))
 			cycleFrame.removeIncrementColumns();
 	}
 	
 	/**
 	 * 
 	 * @param increment	the new Increment value
 	 */
 	public void setIncrement(Increment increment)
 	{
 		if (trendFrame != null)
 			trendFrame.incrementSelector.setSelectedItem(increment);
 		// TODO: set cycle appropriately for increment
 		//if (cycleFrame != null && cycleFrame.isVisible())
 		//	cycleFrame.setVisible(false);
 		if (cycleFrame != null)
 		{
 			if (increment.equals(Increment.DAY))
 				cycleFrame.cycleSelector.setSelectedItem(Cycle.DAY_OF_WEEK);
 			else if (increment.equals(Increment.WEEK))
 				cycleFrame.cycleSelector.setSelectedItem(Cycle.DAY_OF_WEEK);
 			else
 				cycleFrame.cycleSelector.setSelectedItem(Cycle.MONTH_OF_YEAR);
 		}
 	}
}
